﻿using System;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;

namespace Sttplay.Net.UDP
{
    public class IRCTP
    {
        public const string RCTPDLL = "rctp_cpp.dll";

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void ErrLogCallbackDelegate(IntPtr buffer);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void RecvCallbackDelegate(IntPtr buffer, int len, IntPtr sockaddr, int reliable, IntPtr user);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void SendCallbackDelegate(IntPtr message, int state, IntPtr user);


        [DllImport(RCTPDLL, CallingConvention = CallingConvention.Cdecl)]
        private static extern void SetRecvCallback(RecvCallbackDelegate cb);

        [DllImport(RCTPDLL, CallingConvention = CallingConvention.Cdecl)]
        private static extern void SetSendCallback(SendCallbackDelegate cb);

        [DllImport(RCTPDLL, CallingConvention = CallingConvention.Cdecl)]
        private static extern void SetErrorLogCallback(ErrLogCallbackDelegate cb);

        [DllImport(RCTPDLL, CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr RUDPEx_New(IntPtr user);

        [DllImport(RCTPDLL, CallingConvention = CallingConvention.Cdecl)]
        public static extern void RUDPEx_Delete(IntPtr rctp);

        [DllImport(RCTPDLL, CallingConvention = CallingConvention.Cdecl)]
        public static extern int RUDPEx_GetLocalEndPoint(IntPtr rctp, StringBuilder ip, ref int port);

        [DllImport(RCTPDLL, CallingConvention = CallingConvention.Cdecl)]
        public static extern int RUDPEx_JoinMulticastGroup(IntPtr rudp, string group);

        [DllImport(RCTPDLL, CallingConvention = CallingConvention.Cdecl)]
        public static extern void RUDPEx_DropMulticastGroup(IntPtr rudp);

        [DllImport(RCTPDLL, CallingConvention = CallingConvention.Cdecl)]
        public static extern int RUDPEx_Initialize(IntPtr rctp, string ip, int port);

        [DllImport(RCTPDLL, CallingConvention = CallingConvention.Cdecl)]
        public static extern void RUDPEx_Terminate(IntPtr rctp);

        [DllImport(RCTPDLL, CallingConvention = CallingConvention.Cdecl)]
        public static extern void RUDPEx_Update(IntPtr rctp, int timeout);

        [DllImport(RCTPDLL, CallingConvention = CallingConvention.Cdecl)]
        public static extern void RUDPEx_Send(IntPtr rudp, byte[] buffer, int offset, int len, byte[] ip, int port, int reliable);

        [DllImport(RCTPDLL, CallingConvention = CallingConvention.Cdecl)]
        public static extern uint RUDPEx_GetTimestamp();

        [DllImport(RCTPDLL, CallingConvention = CallingConvention.Cdecl)]
        public static extern void RUDPEx_SetOptionInt(IntPtr rudp, int option, uint value);

        [DllImport(RCTPDLL, CallingConvention = CallingConvention.Cdecl)]
        public static extern void RUDPEx_SetOptionFloat(IntPtr rudp, int option, float value);


        private static RecvCallbackDelegate defRecvCallback;
        private static Action<byte[], EndPoint, bool, IntPtr> recvCallback;
        private static void DefaultRecvCallback(IntPtr buffer, int len, IntPtr sockaddr, int reliable, IntPtr user)
        {
            if (recvCallback == null)
                return;
            byte[] bs = new byte[len];
            Marshal.Copy(buffer, bs, 0, len);
            int port = BitConverter.ToUInt16(new byte[] { Marshal.ReadByte(sockaddr, 3), Marshal.ReadByte(sockaddr, 2) }, 0);
            string ip = string.Format("{0}.{1}.{2}.{3}", Marshal.ReadByte(sockaddr, 4), Marshal.ReadByte(sockaddr, 5), Marshal.ReadByte(sockaddr, 6), Marshal.ReadByte(sockaddr, 7));
            recvCallback(bs,
                new IPEndPoint(IPAddress.Parse(ip), port),
                reliable != 0, user);
        }
        public static void SetRecvCallback(Action<byte[], EndPoint, bool, IntPtr> cb)
        {
            if (cb == null)
            {
                defRecvCallback = null;
                recvCallback = null;
            }
            else
            {
                defRecvCallback = DefaultRecvCallback;
                recvCallback = cb;
            }
            SetRecvCallback(defRecvCallback);
        }

        private static SendCallbackDelegate defSendCallback;
        private static Action<IntPtr, RCTP.SendState, IntPtr> sendCallback;
        private static void DefaultSendCallback(IntPtr message, int state, IntPtr user)
        {
            if (sendCallback == null)
                return;
            sendCallback(message, (RCTP.SendState)state, user);
        }
        public static void SetSendCallback(Action<IntPtr, RCTP.SendState, IntPtr> cb)
        {
            if (cb == null)
            {
                defSendCallback = null;
                sendCallback = null;
            }
            else
            {
                defSendCallback = DefaultSendCallback;
                sendCallback = cb;
            }
            SetSendCallback(defSendCallback);
        }


        private static ErrLogCallbackDelegate defErrLogCallback;
        private static Action<string> errLogCallback;
        private static void DefaultErrlogCallback(IntPtr buffer)
        {
            if (errLogCallback == null)
                return;
            errLogCallback(Marshal.PtrToStringAnsi(buffer));
        }
        public static void SetErrLogCallback(Action<string> cb)
        {
            if (cb == null)
            {
                defErrLogCallback = null;
                errLogCallback = null;
            }
            else
            {
                defErrLogCallback = DefaultErrlogCallback;
                errLogCallback = cb;
            }
            SetErrorLogCallback(defErrLogCallback);
        }
    }
}
